package ags.muse.recon;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;

import ags.muse.base.Rules;
import ags.util.points.*;

public class EnemyFiring {
    private final Rules rules;
    private final RobotList robots;

    // VirtualBullet class
    public class VirtualBullet {
        public final AbsolutePoint location;
        public final RelativePoint velocity;
        public final double power;
        public final int duplicates;

        public VirtualBullet update() {
            VirtualBullet newBullet = new VirtualBullet(this, location.addRelativePoint(velocity));
            if (newBullet.location.x < 0 || newBullet.location.y < 0 || newBullet.location.x > rules.BATTLEFIELD_WIDTH || newBullet.location.y > rules.BATTLEFIELD_HEIGHT) {
                return null;
            }
            return newBullet;
        }

        private VirtualBullet(VirtualBullet toClone, AbsolutePoint newLoc) {
            location = newLoc;
            velocity = toClone.velocity;
            power = toClone.power;
            duplicates = toClone.duplicates;
        }

        private VirtualBullet(AbsolutePoint origin, AbsolutePoint target, RelativePoint targetVelocity, double power, double linearFactor, int duplicates) {
            double vmag = rules.getBulletSpeed(power);
            RelativePoint rel = RelativePoint.fromPP(origin, target);

            if (linearFactor != 0) {
                double offset = linearFactor*(targetVelocity.magnitude * Math.sin(targetVelocity.direction - rel.direction) / vmag);
                rel = RelativePoint.fromDM(rel.direction + offset, rel.magnitude);
            }

            this.location = origin;
            this.velocity = RelativePoint.fromDM(rel.direction, vmag);
            this.power = power;
            this.duplicates = duplicates;
        }
    }

    ArrayList<VirtualBullet> bullets = new ArrayList<VirtualBullet>();
    public ArrayList<VirtualBullet> getBullets() { return bullets; }

    // Constructor
    public EnemyFiring(Rules rules, RobotList robots) {
        this.rules = rules;
        this.robots = robots;
    }

    // Update
    public void update() {
        // Process all bots
        for (EnemyRobot e : robots.getEnemies()) {
            updateBot(e);
        }

        // Update bullets  in the air
        ArrayList<VirtualBullet> newBullets = new ArrayList<VirtualBullet>(bullets.size());
        for (VirtualBullet b : bullets) {
            b = b.update();
            if (b != null) {
                newBullets.add(b);
            }
        }
        bullets.clear();
        bullets.addAll(newBullets);
    }

    // Update a specific bot
    private void updateBot(EnemyRobot e) {
        // Don't process if we don't have new data
        if (e.getDataAge() > 1) {
            return;
        }

        // They didn't fire at us if we weren't at least close to as close as the closest
        if (e.getClosestDist() + 50 < e.getDistance(rules.NAME)) {
            return;
        }

        // If too early, ignore
        if (robots.getSelf().getHistory().prev() == null) {
            return;
        }

        // Find the last non-estimated tick
        RobotHistory cursor = e.getHistory().prev();
        RobotHistory scursor = robots.getSelf().getHistory().prev().prev();
        if (cursor == null || scursor == null) {
            return;
        }
        int dupliCount = 1;
        while (cursor.isEstimated()) {
            cursor = cursor.prev();
            scursor = scursor.prev();
            dupliCount++;
        }

        // Get energy change
        double firePower = cursor.getEnergy() - e.getEnergy();

        // Abort if not actually firing
        if (firePower >= 3.01 || firePower <= 0.09) {
            return;
        }

        // Move cursors out of firing tick
        cursor = cursor.next();
        scursor = scursor.next();

        // Add virtual bullets for each possible firing tick
        int ticksBehind = dupliCount - 1;
        while (cursor != null) {
            for (int j = 0; j <= 1; j++) {
                VirtualBullet bullet = (new VirtualBullet(cursor.getLocation(), scursor.getLocation(), scursor.getVelocity(), firePower, j, dupliCount*(1+2*j)));
                for (int i=0; i<ticksBehind && bullet != null; i++) {
                    bullet = bullet.update();
                }
                if (bullet == null) {
                    continue;
                }
                bullets.add(bullet);
            }
            cursor = cursor.next();
            scursor = scursor.next();
            ticksBehind--;
        }
    }

    public void paint(Graphics2D g) {
        g.setColor(Color.cyan);
        for (VirtualBullet b : bullets) {
            AbsolutePoint p = b.location;
            int x = (int)Math.round(p.x);
            int y = (int)Math.round(p.y);
            g.drawLine(x-3, y, x+3, y);
            g.drawLine(x, y-3, x, y+3);
        }
    }
}
